
#include "aligned_heap.h"

#include <cstdlib>


namespace	common
{
	bool	is_2_pow_n( size_t n )
	{
		return (((n) & (n-1)) == 0);
	}

	aligned_heap::aligned_heap()
	{
		heap_ = NULL;
	}

	aligned_heap::~aligned_heap()
	{
		destroy();
	}

	bool	aligned_heap::create( DWORD options )
	{
		assert( heap_ == NULL );
		if( heap_ != NULL )
		{
			return false;
		}

		heap_ = ::HeapCreate( options, 0, 0 );
		if( heap_ == NULL )
		{
			return false;
		}

		return true;
	}

	void	aligned_heap::destroy()
	{
		if( heap_ != NULL )
		{
			::HeapDestroy( heap_ );
			heap_ = NULL;
		}
	}

	void*	aligned_heap::alloc( size_t size, size_t alignment )
	{
		// from msvc++ crt src: align.c
		assert( heap_ != NULL );
		if( heap_ == NULL )
		{
			return NULL;
		}

		assert( is_2_pow_n( alignment ) );
		if( !is_2_pow_n( alignment ) )
		{
			return NULL;
		}

		alignment = (alignment > sizeof( void* ) ? alignment : sizeof( void* )) - 1;

		size_t nonuser_size = sizeof( void* ) + alignment;
		size_t block_size = nonuser_size + size;
		assert( size <= block_size );
		if( size > block_size )
		{
			return NULL;
		}

		uintptr_t ptr = reinterpret_cast< uintptr_t >( ::HeapAlloc( heap_, 0, block_size ) );
		assert( ptr != (uintptr_t)NULL );
		if( ptr == (uintptr_t)NULL )
		{
			return NULL;
		}

		uintptr_t retptr = ((ptr + nonuser_size) & ~alignment);
		(reinterpret_cast< uintptr_t* >( retptr ))[-1] = ptr;

		return reinterpret_cast< void* >( retptr );
	}

	bool	aligned_heap::free( void* p )
	{
		// from msvc++ crt src: align.c
		assert( heap_ != NULL );
		if( p == NULL )
		{
			return true;
		}

		if( heap_ == NULL )
		{
			return false;
		}

		uintptr_t ptr = reinterpret_cast< uintptr_t >( p );

		ptr = (ptr & ~(sizeof( void* ) - 1)) - sizeof( void* );

		ptr = *(reinterpret_cast< uintptr_t* >( ptr ));
		return (::HeapFree( heap_, 0, reinterpret_cast< void* >( ptr ) ) != FALSE);
	}

	size_t	aligned_heap::compact()
	{
		size_t free_size = 0;
		assert( heap_ != NULL );
		if( heap_ != NULL )
		{
			free_size = ::HeapCompact( heap_, 0 );
		}

		return free_size;
	}
}
